##TODO Rate routes, edit routes (mainly set as active/inactive), and better viewing quotas.

from django.shortcuts import render_to_response
from django.core.context_processors import csrf
from django.contrib.auth.models import User
from django import forms
from django.template import RequestContext
from django.http import HttpResponseRedirect
from django.contrib.auth.decorators import login_required
from django.utils.encoding import StrAndUnicode, force_unicode
from django.forms.widgets import RadioFieldRenderer, RadioInput
from django.utils.safestring import mark_safe
from routes.models import Region, Quota, Route, Evaluation, Setter
import operator
from django.contrib.sites.models import Site

VERMIN = (
    ('', 'Choose A Rating'),
    ('V0', 'V0'),
    ('V1', 'V1'),
	('V2', 'V2'),
	('V3', 'V3'),
	('V4', 'V4'),
	('V5', 'V5'),
	('V6', 'V6'),
	('V7', 'V7'),
	('V8', 'V8'),
	('V9', 'V9'),
	('V10', 'V10'),
	('V11', 'V11'),
	('V12', 'V12'),
	('V13', 'V13'),
	('V14', 'V14'),
	('V15', 'V15'),
	('V16', 'V16'),
)

YDS = (
	   ('', 'Choose A Rating'),
	   ('5.0', '5.0'),
	   ('5.1', '5.1'),
	   ('5.2', '5.2'),
	   ('5.3', '5.3'),
	   ('5.4', '5.4'),
	   ('5.5', '5.5'),
	   ('5.6', '5.6'),
	   ('5.7', '5.7'),
	   ('5.8', '5.8'),
	   ('5.9', '5.9'),
	   ('5.10a', '5.10a'),
	   ('5.10b', '5.10b'),
	   ('5.10c', '5.10c'),
	   ('5.10d', '5.10d'),
	   ('5.11a', '5.11a'),
	   ('5.11b', '5.11b'),
	   ('5.11c', '5.11c'),
	   ('5.11d', '5.11d'),
	   ('5.12a', '5.12a'),
	   ('5.12b', '5.12b'),
	   ('5.12c', '5.12c'),
	   ('5.12d', '5.12d'),
	   ('5.13a', '5.13a'),
	   ('5.13b', '5.13b'),
	   ('5.13c', '5.13c'),
	   ('5.13d', '5.13d'),
	   ('5.14a', '5.14a'),
	   ('5.14b', '5.14b'),
	   ('5.14c', '5.14c'),
	   ('5.14d', '5.14d'),
	   ('5.15a', '5.15a'),
	   ('5.15b', '5.15b'),
	   ('5.15c', '5.15c'),
	   ('5.15d', '5.15d'),
	   )

class MyRadioInput(RadioInput):
	"""
	An object used by RadioFieldRenderer that represents a single
	<input type='radio'>.
	"""

	def __unicode__(self):
		return mark_safe(u'%s' % (self.tag(),))

class MyRadioRenderer(RadioFieldRenderer):
	def render(self):
		"""Outputs a <td> for this set of radio fields."""
		return mark_safe(u'\n'.join([u'<td class="qcol">%s</td>'
				% force_unicode(w) for w in self]))

	def __iter__(self):
		for i, choice in enumerate(self.choices):
			yield MyRadioInput(self.name, self.value, self.attrs.copy(), choice, i)

def userIsRouteSetter(user):
	if user.is_staff:
		return True
	if len(Setter.objects.filter(user=user)) == 1:
		return True
	return False

@login_required(login_url='/accounts/login/')
def listRegions(request):
	regions = Region.objects.all()
	return render_to_response("routes/list_regions.html",
		{"regions":regions, "setter": userIsRouteSetter(request.user), "staff": request.user.is_staff},
			context_instance=RequestContext(request))

def getQuotaDict(region):
	quotas = Quota.objects.filter(region=region).order_by('-rating')
	dict = {'ratings':[], 'counts':{}}
	for quota in quotas:
		if not quota.rating in dict['ratings']:
			dict['ratings'].append(quota.rating)
			dict['counts'][quota.rating] = quota.number
		else:
			dict['counts'][quota.rating] += quota.number
	return dict

def getUnQuotas():
	regions = Region.objects.all().order_by('name')
	region_quotas = {}
	region_quotas["list"] = []
	for region in regions:
		quotas = getQuotaDict(region)
		routes = Route.objects.filter(region=region).order_by('-rating','name')
		for route in routes:
			if route.rating in quotas['counts']:
				quota = quotas['counts'][route.rating]
				if not quota == 0:
					quotas['counts'][route.rating] = quota - 1
				else:
					del quotas['counts'][route.rating]
		region_quotas["list"].append(region.name)
		region_quotas[region.name] = (region.id, quotas)
	return region_quotas

def quotaOutput(region_quotas):
	output = []
	for region in region_quotas["list"]:
		output.append("<h2>Region <a href='/routes/addroute/" + str(region_quotas[region][0]) +"'>" + region+"</a></h2>")
		for rating in region_quotas[region][1]["ratings"]:
			output.append("Needs " + str(region_quotas[region][1]["counts"][rating]) + " " + rating + "s")
	return output

@login_required(login_url='/accounts/login/')
def listQuotas(request):
	region_quotas = getUnQuotas()
	output = quotaOutput(region_quotas)
	return render_to_response("routes/list_quotas.html",
		{"output":output},
			context_instance=RequestContext(request))

class quotaForm(forms.Form):
	rating = forms.ChoiceField(label='Rating: ', required=False)
	number = forms.IntegerField(label='Number Desired', required=True)

	def check_for_errors(self, region):
		data = self.cleaned_data
		if data['rating'] == '':
			return 'Error: You must specificy a rating!'
		try:
			Quota.objects.get(region=region, rating=data['rating'].strip())
		except:
			pass
		else:
			return 'Error: This region already has a quota for that rating!'
		if int(data['number']) < 1:
			return 'Error: You must specify a quota greater than zero!'
		return ''


	def save(self, region):
		data = self.cleaned_data
		quota = Quota(region = region, rating=data['rating'].strip(), number=int(data['number']) )
		quota.save()

def populateChoices(region_type, form):
	if region_type == 'H':
		form.fields['rating'].choices = VERMIN
	elif region_type == 'Y':
		form.fields['rating'].choices = YDS
	return form

@login_required(login_url='/accounts/login/')
def addRemoveQuotas(request, region_id):
	if request.user.is_staff:
		region = Region.objects.get(pk=region_id)
		quotas = Quota.objects.filter(region=region)
		add_form = populateChoices(region.type, quotaForm())
		message = ''
		if request.method == 'POST':
			if 'add_quota' in request.POST:
				add_form = populateChoices(region.type, quotaForm(request.POST))
				if add_form.is_valid():
					message = add_form.check_for_errors(region)
					if len(message) == 0 :
						add_form.save(region)
			elif 'del_quota' in request.POST:
				Quota.objects.filter(id__in=request.POST.getlist('quota')).delete()
		return render_to_response("routes/edit_quotas.html",
				{"form":add_form, "quotas":quotas, "message":message, 'region':region,
				'csrf_token':csrf(request)['csrf_token']},
				context_instance=RequestContext(request))
	else:
		return render_to_response("unauth.html", {})


class routeForm(forms.Form):
	rating = forms.ChoiceField(label='Rating', required=False)
	name = forms.CharField(label=(u'Name'), required=True, max_length = 100)
	number = forms.CharField(label=(u'Rope Number'), required=False, max_length = 100)
	color = forms.CharField(label=(u'Color'), required=True, max_length = 100)
	active = forms.BooleanField(label=(u'Active'), required = False)
	image = forms.ImageField(label=(u'Route Image'), required=False)
	description = forms.CharField(label=(u'Description'), required=False, widget=forms.Textarea)

	def check_for_errors(self):
		data = self.cleaned_data
		if data['rating'] == '':
			return 'Error: You must specificy a rating!'
		if (not data['image']) and data['description'] == '':
			return 'Error: You must provide either an image or a description!'
		return ''

	def create(self, region, user):
		data = self.cleaned_data
		route = Route(region = region, name=data['name'], rating = data['rating'], image=data['image'],
					number=data['number'],color=data['color'],description=data['description'], setter=user)
		route.save()

	def save(self, route):
		data = self.cleaned_data
		route.rating = data['rating']
		route.name = data['name']
		route.number = data['number']
		route.color = data['color']
		if data['image']:
			if route.image:
				route.image.delete()
			route.image = data['image']
		route.description = data['description']
		route.active = data['active']
		route.save()

@login_required(login_url='/accounts/login/')
def addRoute(request, region_id):
	if userIsRouteSetter(request.user):
		region = Region.objects.get(pk=region_id)
		message = ''
		if request.method =='POST':
			route_form = populateChoices(region.type, routeForm(request.POST, request.FILES))
			route_form.fields['active'].widget = forms.HiddenInput()
			if route_form.is_valid():
				message = route_form.check_for_errors()
				if len(message) == 0:
					route_form.create(region, request.user)
					return HttpResponseRedirect("/routes/listroutes/" + str(region_id))
		else:
			route_form = populateChoices(region.type, routeForm())
			route_form.fields['active'].widget = forms.HiddenInput()
		return render_to_response("routes/iu_route.html",
				{"form":route_form, "message":message, 'action':'Add a Route',
				"region_name":region.name ,'csrf_token':csrf(request)['csrf_token']},
				context_instance=RequestContext(request))
	else:
		return render_to_response("unauth.html", {})


def popRouteForm(route):
	info = {}
	info["rating"] = route.rating
	info["name"] = route.name
	info["number"] = route.number
	info["color"] = route.color
	info["active"] = route.active
	info["description"] = route.description
	return info

@login_required(login_url='/accounts/login/')
def editRoute(request, route_id):
	route = Route.objects.get(pk=route_id)
	if route.setter.id == request.user.id or request.user.is_staff:
		region = route.region
		message = ''
		if request.method =='POST':
			route_form = populateChoices(region.type, routeForm(request.POST, request.FILES))
			if route_form.is_valid():
				message = route_form.check_for_errors()
				if len(message) == 0:
					route_form.save(route)
					return HttpResponseRedirect("/routes/viewroute/" + str(route_id))
		else:
			route_form = populateChoices(region.type, routeForm(popRouteForm(route)))
		return render_to_response("routes/iu_route.html",
				{"form":route_form, "message":message, 'action':'Edit',
				"region_name":region.name ,'csrf_token':csrf(request)['csrf_token']},
				context_instance=RequestContext(request))
	else:
		return render_to_response("unauth.html", {})

@login_required(login_url='/accounts/login/')
def listRoutes(request, region_id):
	region = Region.objects.get(pk=region_id)
	routes = Route.objects.filter(region=region, active=True)
	return render_to_response("routes/list_routes.html",
		{"region":region,"routes":routes, "setter": userIsRouteSetter(request.user),
		"all_routes":False, "active_routes":True},
			context_instance=RequestContext(request))

@login_required(login_url='/accounts/login/')
def allRoutes(request, active):
	active_routes = False
	if active == '1':
		active_routes = True
	routes = Route.objects.filter(active=active_routes).order_by('-date_set')
	return render_to_response("routes/list_routes.html",
		{"routes":routes, "setter": userIsRouteSetter(request.user),
		"all_routes":True, "active_routes":active_routes},
			context_instance=RequestContext(request))

def getInitialRatingDict():
		ratings = {}
		ratings["Overall Satisfaction:"] = 0
		ratings["Consistent Difficulty:"] = 0
		ratings["Fair Rating:"] = 0
		ratings["Variety of Moves:"] = 0
		ratings["Setter Creativity:"] = 0
		ratings["Movement Flows:"] = 0
		ratings["Accessible Climb:"] = 0
		ratings["Enjoyable Climb:"] = 0
		return ratings

def getRouteInfo(route):
	evals = Evaluation.objects.filter(route=route)
	if len(evals) == 0:
		return None
	suggested = {}
	ratings = getInitialRatingDict()
	num = 0.0
	comments = []
	for eval in evals:
		#Median Suggested Rating
		if not eval.suggested_rating in suggested:
			suggested[eval.suggested_rating] = 0
		suggested[eval.suggested_rating] += 1
		#Mean Ratings
		ratings["Overall Satisfaction:"] += eval.overall
		ratings["Consistent Difficulty:"] += eval.consistent
		ratings["Fair Rating:"] += eval.fair
		ratings["Variety of Moves:"] += eval.variety
		ratings["Setter Creativity:"] += eval.creative
		ratings["Movement Flows:"] += eval.flows
		ratings["Accessible Climb:"] += eval.accessible
		ratings["Enjoyable Climb:"] += eval.enjoyable
		num += 1.0
		#List of Comments
		if not eval.comments == "":
			comments.append(eval.comments)
	#Process Mean Ratings:
	for rating in ratings.iterkeys():
		ratings[rating] = ratings[rating]/num
	#Get median
	median_suggested = max(suggested.iteritems(), key=operator.itemgetter(1))[0]
	print ratings
	return {"median_suggested": median_suggested, "mean_ratings":ratings, "comments":comments}

EVALS = ((1,'1'),
	    (2,'2'),
	    (3,'3'),
	    (4,'4'),
	    (5,'5'),
	    (6,'6'),
	    (7,'7'),
	    (8,'8'),
	    (9,'9'),
	    (10,'10'),
	    )

class EvalForm(forms.Form):
	rating = forms.ChoiceField(label='Suggested Rating', required=False)
	overall = forms.ChoiceField(initial=5,label="I Like this Route",
			choices=EVALS,widget=forms.RadioSelect(renderer=MyRadioRenderer), required=False)
	consistent = forms.ChoiceField(initial=5,label="The Difficulty is Consistent",
			choices=EVALS,widget=forms.RadioSelect(renderer=MyRadioRenderer), required=False)
	fair = forms.ChoiceField(initial=5,label="The Route is Fairly Rated",
			choices=EVALS,widget=forms.RadioSelect(renderer=MyRadioRenderer), required=False)
	variety = forms.ChoiceField(initial=5,label="There Are a Variety of Moves",
			choices=EVALS,widget=forms.RadioSelect(renderer=MyRadioRenderer), required=False)
	creative = forms.ChoiceField(initial=5,label="This Route was Creatively Set",
			choices=EVALS,widget=forms.RadioSelect(renderer=MyRadioRenderer), required=False)
	flows = forms.ChoiceField(initial=5,label="The Sequence Flows",
			choices=EVALS,widget=forms.RadioSelect(renderer=MyRadioRenderer), required=False)
	accessible = forms.ChoiceField(initial=5,label="The Route is Accessible to Many Climbers",
			choices=EVALS,widget=forms.RadioSelect(renderer=MyRadioRenderer), required=False)
	enjoyable = forms.ChoiceField(initial=5,label="The Route is Enjoyable",
			choices=EVALS,widget=forms.RadioSelect(renderer=MyRadioRenderer), required=False)
	comments = forms.CharField(label=(u'Comments'), required=False, widget=forms.Textarea)

	def check_for_errors(self, user, route):
		data = self.cleaned_data
		if data["rating"] == '':
			return "Error: You must suggest a rating for this route"
		try:
			Evaluation.objects.get(user = user, route=route)
		except:
			pass
		else:
			return 'Error: You have already rated this route'
		return ""

	def save(self, user, route):
		data = self.cleaned_data
		eval = Evaluation(route=route, user=user, suggested_rating = data["rating"], overall= data["overall"],
					consistent = data["consistent"], fair=data["fair"], variety=data["variety"],
					creative = data["creative"], flows = data["flows"], accessible = data["accessible"],
					enjoyable = data["enjoyable"], comments = data["comments"])
		eval.save()

@login_required(login_url='/accounts/login/')
def viewRoute(request, route_id):
	setter = userIsRouteSetter(request.user)
	route = Route.objects.get(pk=route_id)
	region = route.region
	message = ""
	eval_form = populateChoices(region.type, EvalForm())
	route_info = getRouteInfo(route)
	return render_to_response("routes/view_route.html",
			{"route":route, "date_set":route.date_set.date(), "route_info":route_info,"setter":setter, 'action':'View'},
				context_instance=RequestContext(request))

@login_required(login_url='/accounts/login/')
def rateRoute(request, route_id):
	route = Route.objects.get(pk=route_id)
	region = route.region
	setter = userIsRouteSetter(request.user)
	message = ""
	if request.method == "POST":
		eval_form = populateChoices(region.type, EvalForm(request.POST))
		if eval_form.is_valid():
			message = eval_form.check_for_errors(request.user, route)
			if len(message) == 0:
				eval_form.save(request.user, route)
				return HttpResponseRedirect("/routes/viewroute/" + str(route_id))
	else:
		eval_form = populateChoices(region.type, EvalForm())
	try:
		Evaluation.objects.get(user=request.user, route=route)
	except:
		if route.active:
			non_radio = [eval_form.fields["rating"].label, eval_form.fields["comments"].label]
			return render_to_response("routes/view_route.html",
				{"message":message, "route":route, "date_set":route.date_set.date(), "setter":setter,
				"action":"Rate", "form":eval_form, "non_radio":non_radio, "evals":EVALS},
				context_instance=RequestContext(request))
		else:
			message = "This route is no longer active."
	else:
		if len(message) == 0:
			message = "You have already rated this route."
	route_info = getRouteInfo(route)
	return render_to_response("routes/view_route.html",
			{"route":route, "date_set":route.date_set.date(), "route_info":route_info, 'action':'View',
			"setter":setter},
				context_instance=RequestContext(request))

@login_required(login_url='/accounts/login/')
def generateQR(request, route_id):
	if userIsRouteSetter(request.user):
		route = Route.objects.get(pk=route_id)
		domain = Site.objects.get_current()
		return render_to_response("routes/qr.html",
				{"route":route, "domain":domain},)
	else:
		return render_to_response("unauth.html", {})